﻿using System;
using System.Collections.Generic;
using System.Reflection;

namespace rehot.core
{
    using SanlieHash;

    public class HotMembers
    {
        internal HotType _hottype { get; set; }
        internal Dictionary<string, FieldInfo> _fields { get; set; }
        internal Dictionary<string, PropertyInfo> _propertys { get; set; }
        internal void Lose()
        {
            _hottype = null;
            if (_fields != null)
            {
                _fields.Clear();
                _fields = null;
            }
            if (_propertys != null)
            {
                _propertys.Clear();
                _propertys = null;
            }
        }
        public HotMembers(HotType hotType)
        {
            this._hottype = hotType;
            var runtime_fields = this._hottype._type.GetFields();
            var runtime_propertys = this._hottype._type.GetProperties();
            this._fields = new Dictionary<string, FieldInfo>(runtime_fields.Length);
            this._propertys = new Dictionary<string, PropertyInfo>(runtime_propertys.Length);
            string id;
            foreach (var field in runtime_fields)
            {
                id = FastHash.GetSM3(field.Name);
                if (! _fields.ContainsKey(id)) _fields.Add(id, field);
                else _fields[id] = field;
            }
            foreach (var property in runtime_propertys)
            {
                id = FastHash.GetSM3(property.Name);
                if (! _propertys.ContainsKey(id)) _propertys.Add(id, property);
                else _propertys[id] = property;
            }
        }
        public IEnumerable<string> AllMemberId()
        {
            foreach (var key in _fields.Keys) yield return key;
            foreach (var key in _propertys.Keys) yield return key;
        }
        public bool Exist(string name)
        {
            return !string.IsNullOrEmpty(FindMemberId(name));
        }
        public string FindMemberId(string name)
        {
            var id = FastHash.GetSM3(name);
            if (_propertys.ContainsKey(id) || _fields.ContainsKey(id)) return id;
            return string.Empty;
        }
        public bool CanWrite(string id)
        {
            if (_propertys.ContainsKey(id)) return _propertys[id].CanWrite;
            else return _fields.ContainsKey(id);
        }
        public bool CanRead(string id)
        {
            if (_propertys.ContainsKey(id)) return _propertys[id].CanRead;
            else return _fields.ContainsKey(id);
        }
        public string ReadNameById(string id)
        {
            if (_propertys.ContainsKey(id)) return _propertys[id].Name;
            else if (_fields.ContainsKey(id)) return _fields[id].Name;
            return string.Empty;
        }
        public object GetByMemberId(object obj, string id)
        {
            if (_propertys.ContainsKey(id))
            {
                if (_propertys[id].CanRead)
                {
                    var method = _propertys[id].GetGetMethod();
                    if (obj == null && !method.IsStatic) throw new ArgumentNullException(id); //非静态需要传入实例
                    return method.Invoke(obj, null);
                }
            }
            if (_fields.ContainsKey(id))
            {
                if (obj == null && !_fields[id].IsStatic) throw new ArgumentNullException(id); //非静态需要传入实例
                return _fields[id].GetValue(obj);
            }
            throw new EntryPointNotFoundException(id);
        }
        public void SetObjByMemberId(object obj, string id, object value)
        {
            if (_propertys.ContainsKey(id))
            {
                if (_propertys[id].CanWrite)
                {
                    var method = _propertys[id].GetSetMethod();
                    if (obj == null && !method.IsStatic) throw new ArgumentNullException(id); //非静态需要传入实例
                    method.Invoke(obj, new object[] { value });
                    return;
                }
            }
            if (_fields.ContainsKey(id))
            {
                if (obj == null && !_fields[id].IsStatic) throw new ArgumentNullException(id); //非静态需要传入实例
                _fields[id].SetValue(obj, value);
                return;
            }
            throw new EntryPointNotFoundException(id);
        }
        public Tout GetByMemberId<Tout>(object obj, string id)
        {
            if (! string.IsNullOrEmpty(id))
            {
                var rlt = this.GetByMemberId(obj, id);
                if (rlt is Tout rlt1) return rlt1;
            }
            return default(Tout);
        }
        public HotObject GetUnknownByMemberId(object obj, string id)
        {
            if (string.IsNullOrEmpty(id)) return HotObject.Null;
            var rlt = this.GetByMemberId(obj, id);
            if (rlt == null) return HotObject.Null;
            return new HotObject(rlt);
        }
        public void SetByMemberId<Tin>(object obj, string id, Tin value)
        {
            if (string.IsNullOrEmpty(id)) return;
            if (value is HotObject hotobj) this.SetObjByMemberId(obj, id, hotobj._obj);
            else this.SetObjByMemberId(obj, id, value);
        }
        public object Get(object obj, string name)
        {
            var id = FastHash.GetSM3(name);
            return GetByMemberId(obj, id);
        }
        public void Set<Tin>(object obj, string name, Tin value)
        {
            var id = FastHash.GetSM3(name);
            SetByMemberId(obj, id, value);
        }
        public void SetObj(object obj, string name, object value)
        {
            var id = FastHash.GetSM3(name);
            SetObjByMemberId(obj, id, value);
        }
        public Tout Get<Tout>(object obj, string name)
        {
            var id = FastHash.GetSM3(name);
            return GetByMemberId<Tout>(obj, id);
        }
        public HotObject GetUnknown(object obj, string name)
        {
            var id = FastHash.GetSM3(name);
            return GetUnknownByMemberId(obj, id);
        }
        internal static readonly string Enum_ValueId = FastHash.GetSM3("value__");
        internal IEnumerable<string> AllEnumMemberId()
        {
            if (_hottype.IsType<Enum>())
                foreach (var key in _fields.Keys)
                if (Enum_ValueId != key) yield return key;
        }
    }
}
